package org.shagin.jfin.ui.budget.item.impl;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

import org.apache.commons.lang3.StringUtils;
import org.shagin.jfin.core.CoreUtils;
import org.shagin.jfin.core.Debug;
import org.shagin.jfin.data.model.IBudget;
import org.shagin.jfin.data.model.OperationCategory;
import org.shagin.jfin.data.model.OperationCategoryLeaf;
import org.shagin.jfin.data.model.OperationRule;
import org.shagin.jfin.ui.UI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class OperationCategoryView.
 */
@SuppressWarnings("serial")
public class OperationCategoryView extends AbstractBudgetItemView {
	private static final Logger log = LoggerFactory
			.getLogger(OperationCategoryView.class);

	protected List<OperationRule> operationRules = new ArrayList<OperationRule>();

	private JTable table = new JTable();
	private JButton btnAdd = new JButton();
	private JButton btnDelete = new JButton();
	private JButton btnEdit = new JButton();
	private OperationRulesTableModel tableModel = new OperationRulesTableModel();

	/**
	 * The Class OperationRulesTableModel.
	 */
	private class OperationRulesTableModel extends AbstractTableModel {

		// TODO Localize
		private String[] columnNames = new String[] { "Category", "Description" };

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int getRowCount() {
			return operationRules.size();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int getColumnCount() {
			return columnNames.length;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Object getValueAt(int rowIndex, int columnIndex) {
			String result = StringUtils.EMPTY;
			OperationRule operationRule = operationRules.get(rowIndex);
			switch (columnIndex) {
			case 0:
				result = operationRule.getCategory().getId();
				break;
			case 1:
				result = operationRule.toString();
				break;
			default:
				break;
			}
			return result;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public String getColumnName(int column) {
			return columnNames[column];
		}
	}

	/**
	 * Instantiates a new operation category view.
	 */
	public OperationCategoryView() {
		super();
		setLayout(new BorderLayout());
		JScrollPane scrollPane = new JScrollPane(table);
		add(createToolbar(), BorderLayout.NORTH);
		add(scrollPane, BorderLayout.CENTER);
		table.setModel(tableModel);
	}

	/**
	 * Creates the toolbar.
	 * 
	 * @return the j panel
	 */
	private JPanel createToolbar() {
		JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, UI.HGAP,
				UI.VGAP));
		// TODO Localize
		btnAdd.setText("Add");
		btnDelete.setText("Delete");
		btnEdit.setText("Edit");
		panel.add(btnAdd);
		panel.add(btnDelete);
		panel.add(btnEdit);
		addListeners();
		return panel;
	}

	/**
	 * Adds the listeners.
	 */
	private void addListeners() {
		btnAdd.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				Debug.traceEnter(log);
				if (isOperationCategoryLeaf()) {
					addOperationCategoryRule();
				} else
					addOperationCategory();
			}
		});

		btnDelete.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				Debug.traceEnter(log);
				if (isOperationCategoryLeaf()) {
					deleteOperationCategoryRule();
				} else
					deleteOperationCategory();
			}
		});

		btnEdit.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				Debug.traceEnter(log);
				if (isOperationCategoryLeaf()) {
					editOperationCategoryRule();
				} else
					editOperationCategory();
			}
		});
	}

	/**
	 * Gets the budget.
	 * 
	 * @return the budget
	 */
	private IBudget getBudget() {
		IBudget result = null;
		OperationCategory operationCategory = getOperationCategory();
		if (operationCategory != null)
			result = operationCategory.getBudget();
		CoreUtils.assertNotNull(result, "budget");
		return result;
	}

	/**
	 * Adds the operation category.
	 */
	private void addOperationCategory() {
		Debug.traceEnter(log);
		OperationCategory parent = getOperationCategory();
		if (parent != null) {
			// TODO Localize
			String title = "New category: " + parent.getId();
			String newCategoryName = JOptionPane.showInputDialog(null,
					StringUtils.EMPTY, title, JOptionPane.QUESTION_MESSAGE);
			if (!StringUtils.isEmpty(newCategoryName)) {
				OperationCategory newCategory = new OperationCategory(parent,
						newCategoryName, getBudget());
				parent.add(newCategory);
				getBudget().fireOperationCategoryAddedEvent(newCategory);
			}
		}
	}

	/**
	 * Adds the operation category rule.
	 */
	private void addOperationCategoryRule() {
		Debug.traceEnter(log);
	}

	/**
	 * Delete operation category.
	 */
	private void deleteOperationCategory() {
		Debug.traceEnter(log);
	}

	/**
	 * Delete operation category rule.
	 */
	private void deleteOperationCategoryRule() {
		Debug.traceEnter(log);
	}

	/**
	 * Edits the operation category.
	 */
	private void editOperationCategory() {
		Debug.traceEnter(log);
	}

	/**
	 * Edits the operation category rule.
	 */
	private void editOperationCategoryRule() {
		Debug.traceEnter(log);
	}

	/**
	 * Load operation rules.
	 * 
	 * @param list
	 *            the list
	 * @param operationCategory
	 *            the operation category
	 */
	protected static void loadOperationRules(List<OperationRule> list,
			OperationCategory operationCategory) {
		CoreUtils.checkNotNull(operationCategory, "operationCategory");
		if (operationCategory instanceof OperationCategoryLeaf) {
			OperationCategoryLeaf operationCategoryLeaf = (OperationCategoryLeaf) operationCategory;
			for (OperationRule operationRule : operationCategoryLeaf.getRules()) {
				list.add(operationRule);
			}
		} else
			for (OperationCategory child : operationCategory.getChilds()) {
				loadOperationRules(list, child);
			}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setBudgetItem(Object budgetItem) {
		Debug.traceEnter(log, budgetItem);
		if (log.isDebugEnabled() && budgetItem != null)
			log.debug("budgetItem.Class:" + budgetItem.getClass().getName());
		this.budgetItem = budgetItem;
		loadOperationRules();
		updateTableUI();
		updateToolbarState();
	}

	/**
	 * Update toolbar state.
	 */
	protected void updateToolbarState() {
		btnAdd.setEnabled(getOperationCategory() != null);
	}

	/**
	 * Checks if is selected node leaf.
	 * 
	 * @return true, if is selected node leaf
	 */
	protected boolean isOperationCategoryLeaf() {
		return budgetItem != null
				&& budgetItem instanceof OperationCategoryLeaf;
	}

	/**
	 * Load operation rules.
	 */
	private void loadOperationRules() {
		CoreUtils.assertNotNull(operationRules, "operationRules");
		CoreUtils.assertNotNull(getOperationCategory(), "operationCategory");
		operationRules.clear();
		loadOperationRules(operationRules, getOperationCategory());
	}

	/**
	 * Gets the operation category.
	 * 
	 * @return the operation category
	 */
	private OperationCategory getOperationCategory() {
		OperationCategory result = null;
		if (budgetItem != null && budgetItem instanceof OperationCategory)
			result = (OperationCategory) budgetItem;
		return result;
	}

	/**
	 * Gets the operation category leaf.
	 * 
	 * @return the operation category leaf
	 */
	private OperationCategoryLeaf getOperationCategoryLeaf() {
		OperationCategoryLeaf result = null;
		if (isOperationCategoryLeaf())
			result = (OperationCategoryLeaf) budgetItem;
		return result;
	}

	/**
	 * Update table ui.
	 */
	protected void updateTableUI() {
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				table.updateUI();
			}
		});
	}
}
